package com.songxh.core.config;

import com.songxh.core.entity.UserContext;
import com.songxh.core.enums.BizCodeEnum;
import com.songxh.core.exception.BizException;
import com.songxh.core.shared.constant.RedisConsts;
import com.songxh.core.utils.JWTUtils;
import com.songxh.core.utils.LoginUtils;
import com.songxh.core.utils.RedisUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 默认拦截器  拦截所有请求
 *
 * @author songxh
 * @create 2022-12-29-16:07
 */
@Component
@Slf4j
public class AccessTokenInterceptor implements HandlerInterceptor {

    private static final String AUTHORIZATION = "Authorization";

    private static String ACCESS_TOKEN = "accessToken";

    public static String BEARER_TYPE = "Bearer";

    //白名单
    @Value("${client.auth.whiteList}")
    public List<String> whiteList;

    //白名单
    @Value("${client.auth.innerToken}")
    public String innerToken;

    /**
     * uri匹配工具
     */
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println(request.getRequestURL());
        log.info(":::: 本机ip ::::" + request.getRemoteAddr());

        //1、白名单直接通过 不验证token，比如登录
        if (checkWhiteUri(request.getRequestURI())) {
            return true;
        }
        //获取request请求中的数据 token
        String accessToken = extractToken(request);
        if (accessToken.equals(innerToken)){
            return true;
        }

        //2、token验证
        try {
            Claims claims = JWTUtils.checkToken(accessToken);
            String account = (String) claims.get("account");
            Long userId = Long.parseLong(claims.get("userId").toString());//不知为何得到的值居然是integer，故而先转string再转long
            String accessTokenKey = String.format(RedisConsts.S_Token_ACCESS_KEY, account, userId);
            String token = RedisUtils.get(accessTokenKey, String.class);
            if (StringUtils.isEmpty(token)) {
                throw new BizException(BizCodeEnum.ACCESS_TOKEN_EXPIRE.getMsg());
            }
            //3、刷新缓存
            JWTUtils.refreshToken(account, userId, RedisConsts.DEFAULT_TOKEN_EXPIRE_TIME_SECONDS);
            //4、验证用户信息
            String userInfoKey = String.format(RedisConsts.S_USER_INFO_KEY, account, userId);
            UserContext userContext = RedisUtils.get(userInfoKey, UserContext.class);
            if (userContext == null) {
                throw new BizException(BizCodeEnum.USER_INFO_NOT_EXIST.getMsg());
            }
            //5、设置上下文
            LoginUtils.setToken(token);
            LoginUtils.put(userContext);
        } catch (Exception e) {
            throw new BizException(String.format(BizCodeEnum.ACCESS_TOKEN_AUTH_ERROR.getMsg(), e.getMessage()));
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //清理数据
        LoginUtils.clearUserInfo();
    }

    /**
     * 验证白名单
     *
     * @param requestUri
     * @return
     */
    private boolean checkWhiteUri(String requestUri) {
        if (whiteList == null) {
            return false;
        }
        if (whiteList.stream().anyMatch((uri) -> antPathMatcher.match(uri, requestUri))) {
            return true;
        }
        return false;
    }

    /**
     * 获取token
     *
     * @param request
     * @return
     */
    private String extractToken(HttpServletRequest request) {
        String auth = request.getHeader(AUTHORIZATION);
        if (StringUtils.isEmpty(auth)) {
            auth = request.getHeader(ACCESS_TOKEN);
        }
        if (StringUtils.isEmpty(auth)) {
            auth = request.getParameter(ACCESS_TOKEN);
        }
        if (StringUtils.isEmpty(auth)) {
            log.info("token为空");
            throw new BizException(BizCodeEnum.ACCESS_TOKEN_IS_NULL.getMsg());
        }
        if ((auth.toLowerCase().startsWith(BEARER_TYPE.toLowerCase()))) {
            String authHeaderValue = auth.substring(BEARER_TYPE.length()).trim();
            return authHeaderValue;
        }
        return auth;
    }
}
